package cn.fcj.f20240328.fsocket.base;

import cn.fcj.f20240328.fsocket.chanelhandler.DEFSocketEventHandlerImp;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;

/**
 * @项目名称:FSocket
 * @功能描述:socket服务类的基础类，可继承此类扩展其他功能
 * @开发　者:mochhk
 * @邮　　箱:mochhk@163.com
 * @CreateDate:2024/4/5 11:25
 */
public abstract class FBaseNIOSocket<T extends SelectableChannel> implements Runnable{
    public enum ErrorCode{
        SOCKET_START_FAIL,
        SOCKET_DISCONNECT,
    }
    protected String ip="";//ip地址
    protected int port = 8888;//端口号
    protected volatile boolean isRuning=false;//线程运行标志
    public volatile boolean isRegistering=false;//注册时，停止select()
    protected Selector mSelector;//通道选择器
    protected T mChannel;//打开通道
    protected ISocketEventHandler socketEventHandler;//socket数据处理类

    public FBaseNIOSocket(){
        socketEventHandler= new DEFSocketEventHandlerImp();
    }

    public FBaseNIOSocket setPort(int port) {
        this.port = port;
        return this;
    }
    public String getIP(){
        return ip;
    }
    public FBaseNIOSocket setIp(String ip){
        this.ip=ip;
        return this;
    }
    public boolean isRuning() {
        return isRuning;
    }

    public FBaseNIOSocket setRuning(boolean isRuning) {
        this.isRuning = isRuning;
        return this;
    }

    /**
     * 设置socket事件的处理方法
     * @param socketEventHandler
     */
    public FBaseNIOSocket setSocketEventHandler(ISocketEventHandler socketEventHandler){
        this.socketEventHandler=socketEventHandler;
        return this;
    }

    /**
     * 启动socket
     */
    public void startSocket(){
        isRuning=true;
        new Thread(this).start();
    }

    /**
     * 加入线程池中启动socket
     * @param workPool
     */
    public void startSocketForWorkPool(ExecutorService workPool){
        workPool.execute(this);
    }

    /**
     * 停止socket
     */
    public void stopSocket(){
        isRuning=false;
        closeSelectorAndChannl();
    }
    /**
     * socket停止时关闭资源
     */
    public void closeSelectorAndChannl(){
        try {
            if(mSelector!=null) {
                mSelector.close();
            }
            if(mChannel!=null){
                mChannel.close();
            }
        } catch (IOException ioe) {
        }
    }
    @Override
    public void run() {
        try {
            initSocket();
        } catch (IOException e) {
            exceptionHandler(e,ErrorCode.SOCKET_START_FAIL,null);
            isRuning=false;
            return;
        }
        do {
            SelectionKey key = null;
            try {
                if (select() <= 0) {
                   continue;
                }
                //通道编号集合
                Iterator<SelectionKey> keys = mSelector.selectedKeys().iterator();
                //遍历
                while (keys.hasNext()) {
                    key = keys.next();
                    //当前key需要从集合中移出
                    keys.remove();
                    //5.判断通道是否有效
                    if (key.isValid()) {
                        try {
                            //判断接受事件
                            connectionHandler(key);
                            channelEvenHandler(key);
                        } catch (CancelledKeyException e) {
                            exceptionHandler(e,ErrorCode.SOCKET_DISCONNECT,key);
//                            //出现异常断开连接
//                            key.cancel();
                        }
                    }
                }
            } catch (IOException e) {
                exceptionHandler(e,ErrorCode.SOCKET_DISCONNECT,key);
            }
            catch (ClosedSelectorException e){
                exceptionHandler(e,ErrorCode.SOCKET_DISCONNECT,key);
            }
        }while (isRuning);
    }

    /**
     * 初始化socket，因服务器端和客户端打开连接不一样所以要重写此方法
     */
    public abstract void initSocket() throws IOException;
    /**
     * 重写此方法，服务器端处理accept事件，客户端处理Connectable事件
     * @param key
     */
    public void connectionHandler(SelectionKey key)throws IOException{
        if(socketEventHandler!=null){
            socketEventHandler.onConnected(key);
        }
    }



    /**
     * 通道事件处理，子类要重写此方法
     */
    public void channelEvenHandler(SelectionKey key) throws CancelledKeyException, IOException {
        if(key.isReadable()){
            if(socketEventHandler!=null){
                socketEventHandler.receiveDataForChannel(key);
            }
        }
        if(key.isWritable()){
            writDataForChannel(key);
        }
    }

    /**
     * 有异常时，需要处理，则重写此方法
     * @param e
     * @param errorCode
     */
    public void exceptionHandler(Exception e,ErrorCode errorCode,SelectionKey key){
        if(socketEventHandler!=null){
            socketEventHandler.exceptionHandler(e,errorCode,key);
        }
    }

    /**
     * selector 选择通道,子selector时，需要用到超时间，可以重写此类
     * @return
     * @throws IOException
     */
    public int select() throws IOException {
        if(isRegistering){
//            System.out.println("-------------多线程事件处理运行中----注册时--wait()---------");
            return -1;
        }
        return mSelector.select();
    }

    /**
     * 默认写入数据，发送出去。有其他需要的，子类可重写。
     * @param key
     */
    public void writDataForChannel(SelectionKey key) throws IOException{
        SocketChannel channel;
        channel = (SocketChannel) key.channel();
        ByteBuffer writeBuffer=(ByteBuffer) key.attachment();
        writeBuffer.flip();
        channel.write(writeBuffer);
        channel.register(key.selector(), SelectionKey.OP_READ);
        Selector selector = key.selector();
        selector.wakeup();
        System.out.println("channelWritable======写入成功===ip==:"+channel.getRemoteAddress());
    }

    /**
     * 发送消息
     * @param key
     * @param msg
     */
    public void sendDataForChannel(SelectionKey key,byte[] msg){
        if(socketEventHandler!=null){
            try {
                ByteBuffer buffer=socketEventHandler.sendData(key,msg);
                if(key!=null){
                    SelectableChannel channel=key.channel();
                    channel.register(key.selector(),SelectionKey.OP_WRITE,buffer);
                    key.selector().wakeup();
                }
                else{
                    if(mChannel instanceof SocketChannel){
                        mChannel.register(mSelector,SelectionKey.OP_WRITE,buffer);
                    }
                }
                mSelector.wakeup();
            } catch (IOException e) {
                exceptionHandler(e,ErrorCode.SOCKET_DISCONNECT,key);
            }
        }
    }
    /**
     * 发送消息
     * @param key
     * @param msg
     */
    public void sendDataForChannel(SelectionKey key,String msg){
        sendDataForChannel(key,msg.getBytes());
    }

    /**
     * 注册焦点事件
     * @param key
     * @param eventCode
     * @return
     */
    public  SelectionKey registerEvnet(SelectionKey key, int eventCode){
        try {
            isRegistering=true;
            SelectableChannel channel=key.channel();
            SelectionKey newkey=channel.register(mSelector,eventCode);
            isRegistering=false;
            mSelector.wakeup();

            return newkey;
        } catch (ClosedChannelException e) {
            exceptionHandler(e,ErrorCode.SOCKET_DISCONNECT,key);
        }
        return null;
    }
    /**
     * 注册焦点事件
     * @param channel
     * @param eventCode
     * @return
     */
    public  SelectionKey registerEvnet(SocketChannel channel, int eventCode){
        try {
            isRegistering=true;
            SelectionKey newkey=channel.register(mSelector,eventCode);
            isRegistering=false;
            mSelector.wakeup();

            return newkey;
        } catch (ClosedChannelException e) {
            exceptionHandler(e,ErrorCode.SOCKET_DISCONNECT,null);
        }
        return null;
    }

    /**
     * 注册焦点事件
     * @param key
     * @param eventCode
     * @param attr
     * @return
     */
    public  SelectionKey registerEvnet(SelectionKey key, int eventCode,Object attr){
        try {
            isRegistering=true;
            SelectableChannel channel=key.channel();
            SelectionKey newkey=channel.register(this.mSelector,eventCode,attr);
            isRegistering=false;
            mSelector.wakeup();

            return newkey;
        } catch (ClosedChannelException e) {
            exceptionHandler(e,ErrorCode.SOCKET_DISCONNECT,key);
        }
        return null;
    }
    /**
     * 注册焦点事件
     * @param channel
     * @param eventCode
     * @param attr
     * @return
     */
    public  SelectionKey registerEvnet(SocketChannel channel, int eventCode,Object attr){
        try {
            isRegistering=true;
            SelectionKey newkey=channel.register(this.mSelector,eventCode,attr);
            isRegistering=false;
            mSelector.wakeup();
            return newkey;
        } catch (ClosedChannelException e) {
            exceptionHandler(e,ErrorCode.SOCKET_DISCONNECT,null);
        }
        return null;
    }

}
